using Adoor.Object.Domain;

#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */ 
#endregion

namespace Adoor.Object.Relational
{
	using System;
	using System.Diagnostics;
	using System.Collections.Generic;
	using Adoor.Object;
	using Adoor.Object.Entity;

	// distinguish types based on the prefix of the string
	public class PrefixKeyManager: IPolymorphismManager
	{
		//protected static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private SortedList<object, IOidData> oidDatas = new SortedList<object, IOidData>();
		public PrefixKeyManager()
		{
		}

		public void AddOidData(IOidData oidData)
		{
			if(AdoorLoger.IsDebugEnabled)
			{
				int oldVal = this.oidDatas.Count;
				
                object discriminator = ((IOidFactory)oidData).Discriminator;
				this.oidDatas[discriminator] = oidData; // TODO: temp cast
				int newVal = this.oidDatas.Count;

                AdoorLoger.Debug("AddedOidData : " + discriminator + " cnt : " + oldVal + " -> " + newVal);
                //log.Debug("AddedOidData : " + discriminator + " cnt : " + oldVal + " -> " + newVal);
			}
			else
			{
				this.oidDatas[((IOidFactory)oidData).Discriminator] = oidData; // TODO: temp cast
			}
		}

		public virtual EntityData GetActualEntityData(string oid)
		{
			return GetActualEntityData(oid, true);
		}

		public EntityData GetActualEntityData(string oid, bool throwIfNotFound)
		{
			if (oid == null)
				return null;
			// dichotomy on KeyFactory.KeyPrefix
			int min = 0;
			int max = this.oidDatas.Count - 1;
			IList<IOidData> values = this.oidDatas.Values;
			while (min <= max)
			{
				int pivot = (min + max) / 2;
				string prefix = (string)this.oidDatas.Keys[pivot];
				if (string.Compare(prefix, oid) > 0)
				{
					if (pivot == max)
						break;
					max = pivot;
				}
				else 
				{
					if (prefix.Length <= oid.Length && oid.Substring(0, prefix.Length) == prefix)
						return ((IOidData)values[pivot]).EntityData;
					if (pivot == min)
					{
						if (min == max)
							break;
						else
							min = max;
					}
					else
						min = pivot;
				}
			}
			if(throwIfNotFound)
				throw new ArgumentException("bad key", "oid");
			return null;
		}
	}

	// distinguish types based on the suffix of the string
	public class SuffixKeyManager: IPolymorphismManager
	{
		//protected static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private SortedList<object, IOidData> oidDatas = new SortedList<object, IOidData>();
		public SuffixKeyManager()
		{
		}

		public void AddOidData(IOidData oidData)
		{
			if(AdoorLoger.IsDebugEnabled)
			{
				int oldVal = this.oidDatas.Count;
				
                object discriminator = ((IOidFactory)oidData).Discriminator;
				this.oidDatas[discriminator] = oidData; // TODO: temp cast
				int newVal = this.oidDatas.Count;

                AdoorLoger.Debug("AddedOidData : " + discriminator + " cnt : " + oldVal + " -> " + newVal); 
                //log.Debug("AddedOidData : " + discriminator + " cnt : " + oldVal + " -> " + newVal);
			}
			else
			{
				this.oidDatas[((IOidFactory)oidData).Discriminator] = oidData; // TODO: temp cast
			}
		}

		public virtual EntityData GetActualEntityData(string oid)
		{
			return GetActualEntityData(oid, true);
		}

		public virtual EntityData GetActualEntityData(string oid, bool throwIfNotFound)
		{
			if (oid == null || oid == string.Empty)
				return null;
			
			// dichotomy on KeyFactory.KeyPrefix
			int min = 0;
			int max = this.oidDatas.Count - 1;
            IList<IOidData> values = this.oidDatas.Values;
			while (min <= max)
			{
				int pivot = (min + max) / 2;
				string suffix = (string) this.oidDatas.Keys[pivot];
				
				string id = oid.Substring(oid.Length - suffix.Length, suffix.Length);
				if (string.Compare(suffix, id) == 0)
					return ((IOidData)values[pivot]).EntityData;
				else if (string.Compare(suffix, id) > 0)
				{
					if (pivot == max)
						break;
					max = pivot;
				}
				else 
				{					
					if (pivot == min)
					{
						if (min == max)
							break;
						else
							min = max;
					}
					else
						min = pivot;
				}
			}
			if(throwIfNotFound)
				throw new ArgumentException("bad key", "oid");
			return null;
		}
	}

	// distinguish types based on the most significant bits of the oid
	public class SmartKeyManager: IPolymorphismManager
	{
		//protected static log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private SortedList<object, IOidData> oidDatas = new SortedList<object, IOidData>();
		public SmartKeyManager()
		{
		}

		public void AddOidData(IOidData oidData)
		{
			if(AdoorLoger.IsDebugEnabled)
			{
				int oldVal = this.oidDatas.Count;
				object discriminator = ((IOidFactory)oidData).Discriminator;
				this.oidDatas[discriminator] = oidData; // TODO: temp cast
				int newVal = this.oidDatas.Count;

                AdoorLoger.Debug("AddedOidData : " + discriminator + " cnt : " + oldVal + " -> " + newVal); 
			}
			else
			{
				this.oidDatas[((IOidFactory)oidData).Discriminator] = oidData; // TODO: temp cast
			}
		}

		public virtual EntityData GetActualEntityData(string oid)
		{
			return GetActualEntityData(oid, true);
		}

		public virtual EntityData GetActualEntityData(string oid, bool throwIfNotFound)
		{
			int v = int.Parse(oid) >> SmartKeyFactory<object>.MaskLength;
			IOidData oidData = (IOidData)this.oidDatas[v];
			if (oidData == null)
			{
				if(throwIfNotFound)
					throw new ArgumentException("bad oid", "oid");
				return null;
			}
			return oidData.EntityData;
		}
	}
}